package com.jetwinner.webfast.mvc.controller;

import com.jetwinner.toolbag.ArrayToolkit;
import com.jetwinner.toolbag.FileToolkit;
import com.jetwinner.util.*;
import com.jetwinner.webfast.image.ImageSize;
import com.jetwinner.webfast.image.ImageUtil;
import com.jetwinner.webfast.kernel.AppUser;
import com.jetwinner.webfast.kernel.FastAppConst;
import com.jetwinner.webfast.kernel.Paginator;
import com.jetwinner.webfast.kernel.dao.support.OrderBy;
import com.jetwinner.webfast.kernel.exception.ActionGraspException;
import com.jetwinner.webfast.kernel.exception.RuntimeGoingException;
import com.jetwinner.webfast.kernel.service.AppUserService;
import com.jetwinner.webfast.kernel.typedef.ParamMap;
import com.jetwinner.webfast.module.bigapp.service.AppGroupService;
import com.jetwinner.webfast.module.bigapp.service.AppGroupThreadService;
import com.jetwinner.webfast.mvc.BaseControllerHelper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.jetwinner.webfast.mvc.BaseControllerHelper.createMessageResponse;
import static com.jetwinner.webfast.mvc.BaseControllerHelper.setFlashMessage;

/**
 * @author xulixin
 */
@Controller("webfastSiteGroupController")
public class GroupController {

    private final AppUserService userService;
    private final AppGroupService groupService;
    private final AppGroupThreadService threadService;
    private final FastAppConst appConst;

    public GroupController(AppUserService userService,
                           AppGroupService groupService,
                           AppGroupThreadService threadService,
                           FastAppConst appConst) {

        this.userService = userService;
        this.groupService = groupService;
        this.threadService = threadService;
        this.appConst = appConst;
    }

    @RequestMapping("/group")
    public ModelAndView indexPage(HttpServletRequest request) {
        Map<String, Map<String, Object>> myJoinGroup = MapUtil.newHashMap(0);

        List<Map<String, Object>> activeGroup = groupService.searchGroups(
                FastHashMap.build(1).add("status", "open").toMap(),
                OrderBy.build(1).addDesc("memberNum"),0, 12);

        List<Map<String, Object>> recentlyThread = threadService.searchThreads(
                FastHashMap.build(2)
                        .add("createdTime", System.currentTimeMillis() - 30*24*60*60*1000L)
                        .add("status", "open").toMap(),
                this.filterSort("byCreatedTimeOnly"),0, 25);

        Set<Object> ownerIds = ArrayToolkit.column(recentlyThread, "userId");
        Set<Object> groupIds = ArrayToolkit.column(recentlyThread, "groupId");
        Set<Object> userIds =  ArrayToolkit.column(recentlyThread, "lastPostMemberId");

        Map<String, AppUser> lastPostMembers = userService.findUsersByIds(userIds);

        Map<String, AppUser> owners = userService.findUsersByIds(ownerIds);

        Map<String, Map<String, Object>> groups = groupService.getGroupsByIds(groupIds);

        AppUser user = AppUser.getCurrentUser(request);

        if (user.getId() != null && user.getId() > 0) {

            int membersCount = groupService.searchMembersCount(
                    FastHashMap.build().add("userId", user.getId()).toMap());

            int start = membersCount > 12 ? (int) (Math.random()*(membersCount - 12)) : 0;

            List<Map<String, Object>> members = groupService.searchMembers(
                    FastHashMap.build().add("userId", user.getId()).toMap(),
                    OrderBy.build(1).addDesc("createdTime"),
                    start, 12);

            groupIds = ArrayToolkit.column(members, "groupId");

            myJoinGroup = groupService.getGroupsByIds(groupIds);
        }

        List<Map<String, Object>> newGroups = groupService.searchGroups(
                FastHashMap.build(1).add("status", "open").toMap(),
                OrderBy.build(1).addDesc("createdTime"), 0, 8);

        return new ModelAndView("group/index")
                .addObject("activeGroup", activeGroup)
                .addObject("myJoinGroup", myJoinGroup)
                .addObject("lastPostMembers", lastPostMembers)
                .addObject("owners", owners)
                .addObject("newGroups", newGroups)
                .addObject("groupinfo", groups)
                .addObject("user", user)
                .addObject("recentlyThread", recentlyThread);
    }

    @RequestMapping("/group/search-group")
    public String searchPage(HttpServletRequest request, Model model) {
        String keyWord = request.getParameter("keyWord");
        Map<String, Object> conditions = FastHashMap.build(2).add("title", keyWord).add("status", "open").toMap();
        Paginator paginator = new Paginator(request, groupService.searchGroupsCount(conditions), 24);

        model.addAttribute("groups", groupService.searchGroups(conditions,
                OrderBy.build(1).addDesc("createdTime"),
                paginator.getOffsetCount(),
                paginator.getPerPageCount()));
        model.addAttribute("paginator", paginator);
        return "/group/search";
    }

    @RequestMapping("/group/add")
    public ModelAndView addGroupAction(HttpServletRequest request) {
        AppUser user = AppUser.getCurrentUser(request);
        if (!user.hasAnyRole("ROLE_ADMIN")) {
            return createMessageResponse("info", "目前只允许管理员创建小组!");
        }

        ModelAndView mav = new ModelAndView("/group/group-add");
        if ("POST".equalsIgnoreCase(request.getMethod())) {

            Map<String, Object> mygroup = ParamMap.toFormDataMap(request);

            Object title = mygroup.get("group[grouptitle]");
            if (EasyStringUtil.isBlank(title)) {
                setFlashMessage("danger", "小组名称不能为空！", request.getSession());
                return mav;
            }

            Map<String, Object> group = FastHashMap.build()
                    .add("title", mygroup.get("group[grouptitle]"))
                    .add("about", mygroup.get("group[about]"))
                    .toMap();

            groupService.addGroup(user, group);
            mav.setViewName("redirect:/group/" + group.get("id") + "/setting/logo");
        }

        return mav;
    }

    @RequestMapping("/group/{id}")
    public ModelAndView groupIndexAction(@PathVariable Integer id, HttpServletRequest request) {
        Map<String, Object> group = groupService.getGroup(id);

        AppUser groupOwner = userService.getUser(group.get("ownerId"));

        if ("close".equalsIgnoreCase(String.valueOf(group.get("status")))) {
            return createMessageResponse("info", "该小组已被关闭");
        }

        List<Map<String, Object>> recentlyJoinMember = groupService.searchMembers(
                FastHashMap.build(1).add("groupId", id).toMap(),
                OrderBy.build(1).addDesc("createdTime"), 0, 12);

        Set<Object> memberIds = ArrayToolkit.column(recentlyJoinMember, "userId");

        AppUser user = AppUser.getCurrentUser(request);

        Map<String, Object> userIsGroupMember = groupService.getMemberByGroupIdAndUserId(id, user.getId());
        Map<String, AppUser> recentlyMembers = userService.findUsersByIds(memberIds);

        Map<String, Object> filters = this.getThreadSearchFilters(request);

        Map<String, Object> conditions = this.convertFiltersToConditions(id, filters);

        Paginator paginator = new Paginator(request,
                threadService.searchThreadsCount(conditions),
                ValueParser.parseInt(conditions.get("num")));

        List<Map<String, Object>> threads = threadService.searchThreads(
                conditions,
                this.filterSort(filters.get("sort")),
                paginator.getOffsetCount(),
                paginator.getPerPageCount()
        );

        Set<Object> ownerIds = ArrayToolkit.column(threads, "userId");

        Set<Object> userIds = ArrayToolkit.column(threads, "lastPostMemberId");

        Map<String, AppUser> owners = userService.findUsersByIds(ownerIds);

        Map<String, AppUser> lastPostMembers = userService.findUsersByIds(userIds);

        List<Map<String, Object>> activeMembers = groupService.searchMembers(FastHashMap.build(2)
                        .add("groupId", id).add("role", "member").toMap(),
                OrderBy.build(1).addDesc("postNum"), 0, 12);

        memberIds = ArrayToolkit.column(activeMembers, "userId");

        Map<String, AppUser> members = userService.findUsersByIds(memberIds);

        ModelAndView mav = new ModelAndView("/group/group-index");
        mav.addObject("groupinfo", group);
        mav.addObject("is_groupmember", this.getGroupMemberRole(id, user));
        mav.addObject("recentlyJoinMember", recentlyJoinMember);
        mav.addObject("owner", owners);
        mav.addObject("user", user);
        mav.addObject("groupOwner", groupOwner);
        mav.addObject("id", id);
        mav.addObject("threads", threads);
        mav.addObject("paginator", paginator);
        mav.addObject("condition", filters);
        mav.addObject("lastPostMembers", lastPostMembers);
        mav.addObject("userIsGroupMember", userIsGroupMember);
        mav.addObject("members", recentlyMembers);
        return mav;
    }

    private OrderBy filterSort(Object sort) {
        OrderBy orderBy = OrderBy.build(0);
        switch (String.valueOf(sort)) {
            case "byPostNum":
                orderBy = OrderBy.build(3)
                        .addDesc("isStick")
                        .addDesc("postNum")
                        .addDesc("createdTime");
                break;
            case "byStick":
            case "byCreatedTime":
                orderBy = OrderBy.build(2)
                        .addDesc("isStick")
                        .addDesc("createdTime");
                break;
            case "byLastPostTime":
                orderBy = OrderBy.build(2)
                        .addDesc("isStick")
                        .addDesc("lastPostTime");
                break;
            case "byCreatedTimeOnly":
                orderBy = OrderBy.build(1)
                        .addDesc("createdTime");
                break;
            default:
                throw new RuntimeGoingException("参数sort不正确。");
        }
        return orderBy;
    }

    private Map<String, Object> getThreadSearchFilters(HttpServletRequest request) {
        Map<String, Object> filters = FastHashMap.build().toMap();
        filters.put("type", request.getParameter("type"));
        if (!ArrayUtil.inArray(filters.get("type"), "all", "elite", "reward")) {
            filters.put("type", "all");
        }
        filters.put("sort", request.getParameter("sort"));

        if (!ArrayUtil.inArray(filters.get("sort"), "byCreatedTime", "byLastPostTime", "byPostNum")) {
            filters.put("sort", "byCreatedTime");
        }
        filters.put("num", request.getParameter("num"));

        if (!ArrayUtil.inArray(filters.get("num"), "25")) {
            filters.put("num", 25);
        }
        return filters;
    }

    private Map<String, Object> convertFiltersToConditions(Integer id, Map<String, Object> filters) {
        Map<String, Object> conditions = FastHashMap.build().add("groupId", id).add("num", 10).add("status", "open").toMap();
        switch (String.valueOf(filters.get("type"))) {
            case "elite":
                conditions.put("isElite", 1);
                break;
            case "reward":
                conditions.put("type", "reward");
                break;
            default:
                break;
        }
        conditions.put("num", filters.get("num"));
        return conditions;
    }

    @RequestMapping("/group/{id}/setting/logo")
    public ModelAndView setGroupLogoAction(HttpServletRequest request, @PathVariable Integer id) {
        AppUser user = AppUser.getCurrentUser(request);

        Map<String, Object> group = groupService.getGroup(id);
        if (!this.checkManagePermission(id, user)) {
            return createMessageResponse("info", "您没有权限!");
        }

        ModelAndView mav = new ModelAndView("/group/setting-logo");
        if ("POST".equalsIgnoreCase(request.getMethod())) {

            String fileName = null;
            try {
                fileName = this.setLogo(request, user);
            } catch (ActionGraspException e) {
                return BaseControllerHelper.createMessageResponse(e.getType(), e.getMessage());
            }

            String baseUrl = "/group/setting/logoCrop/" + fileName + "/" + id;
            mav.setViewName("redirect:" + BaseControllerHelper.generateUrl(baseUrl, FastHashMap.build(2)
                    .add("page", "logoCrop").add("type", "logo").toMap()));
            return mav;
        }

        mav.addObject("groupinfo", group);
        mav.addObject("is_groupmember", this.getGroupMemberRole(id, user));
        mav.addObject("id", id);
        mav.addObject("logo", group.get("logo"));
        mav.addObject("backgroundLogo", group.get("backgroundLogo"));
        return mav;
    }

    private Integer getGroupMemberRole(Integer groupId, AppUser user) {
        if (user.getId() == null) {
            return 0;
        }
        if (groupService.isOwner(groupId, user.getId())) {
            return 2;
        }
        if (groupService.isAdmin(groupId, user.getId())) {
            return 3;
        }
        if (groupService.isMember(groupId, user.getId())) {
            return 1;
        }

        return 0;
    }

    private String setLogo(HttpServletRequest request, AppUser user) throws ActionGraspException {
        MultipartHttpServletRequest multipartReq = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartReq.getFile("form[avatar]");

        if (!FileToolkit.isImageFile(file.getOriginalFilename())) {
            throw new ActionGraspException("info", "上传图片格式错误，请上传jpg, gif, png格式的文件。!");

        }
        String filenamePrefix = "user_" + user.getId() + "_";
        String hash = FileToolkit.hashFilename(filenamePrefix);
        String ext = FileToolkit.getFileExtension(file.getOriginalFilename());
        String filename = filenamePrefix + hash + "." + ext;

        String directory = appConst.getUploadPublicDirectory() + "/tmp/";
        try {
            FileToolkit.transferFile(file, directory, filename);
        } catch (IOException e) {
            throw new ActionGraspException("error",
                    String.format("图片上传失败，请检查上传目录(%s)或文件(%s)是否存在。", directory, filename));
        }

        filename = filename.replace(".", "!");
        return filename;
    }

    private boolean checkManagePermission(Integer id, AppUser user) {
        if (user.hasAnyRole("ROLE_ADMIN")) {
            return true;
        }
        if (groupService.isOwner(id, user.getId())) {
            return true;
        }
        if (groupService.isAdmin(id, user.getId())) {
            return true;
        }
        return false;
    }

    @RequestMapping("/group/setting/logoCrop/{file}/{id}")
    public ModelAndView groupSetLogoCropAction(@PathVariable String file,
                                               @PathVariable Integer id,
                                               HttpServletRequest request) {

        Map<String, Object> group = groupService.getGroup(id);
        AppUser currentUser = AppUser.getCurrentUser(request);

        if (!this.checkManagePermission(id, currentUser)) {
            return createMessageResponse("info", "您没有权限!");
        }
        ModelAndView mav = new ModelAndView();

        String filename = file.replace("!", ".");
        filename = filename.replaceAll("\\.\\.|/|\\\\", "");

        String pictureFilePath = appConst.getUploadPublicDirectory() + "/tmp/" + filename;

        if ("POST".equalsIgnoreCase(request.getMethod())) {
            Map<String, Object> options = ParamMap.toQueryAllMap(request);
            StringBuilder uri = new StringBuilder("redirect:/group/");
            uri.append(id).append("/setting/");
            if ("backGroundLogoCrop".equalsIgnoreCase(request.getParameter("page"))) {
                groupService.changeGroupBackgroundLogo(id, pictureFilePath, options);
                uri.append("backgroundlogo");
            } else {
                groupService.changeGroupLogo(id, pictureFilePath, options);
                uri.append("logo");
            }
            mav.setViewName(uri.toString());
            return mav;
        }

        ImageSize naturalSize = null;
        try {
            naturalSize = ImageUtil.getNaturalSize(new File(pictureFilePath));
        } catch (Exception e) {
            return createMessageResponse("error", "该文件为非图片格式文件，请重新上传。");
        }

        ImageSize scaledSize;
        if ("backGroundLogoCrop".equalsIgnoreCase(request.getParameter("page"))) {
            scaledSize = new ImageSize(1070, 240);
        } else {
            scaledSize = new ImageSize(270, 270);
        }

        String pictureUrl = "tmp/" + filename;

        mav.addObject("groupinfo", group);
        mav.addObject("is_groupmember", this.getGroupMemberRole(id, currentUser));
        mav.addObject("pictureUrl", pictureUrl);
        mav.addObject("naturalSize", naturalSize);
        mav.addObject("scaledSize", scaledSize);
        mav.setViewName("/group/setting-logo-crop");
        return mav;
    }

    @RequestMapping("/group/{id}/setting/info")
    public ModelAndView groupSetAction(@PathVariable Integer id, HttpServletRequest request) {
        AppUser user = AppUser.getCurrentUser(request);
        if (!this.checkManagePermission(id, user)) {
            return createMessageResponse("info", "您没有权限!");
        }

        Map<String, Object> group = groupService.getGroup(id);
        return new ModelAndView("/group/setting-info")
                .addObject("groupinfo", group)
                .addObject("is_groupmember", this.getGroupMemberRole(id, user))
                .addObject("id", id)
                .addObject("logo", group.get("logo"))
                .addObject("backgroundLogo", group.get("backgroundLogo"));
    }

    @RequestMapping("/group/{id}/setting/backgroundlogo")
    public ModelAndView setGroupBackgroundLogoAction(@PathVariable Integer id, HttpServletRequest request) {
        AppUser user = AppUser.getCurrentUser(request);
        if (!this.checkManagePermission(id, user)) {
            return createMessageResponse("info", "您没有权限!");
        }

        if ("POST".equalsIgnoreCase(request.getMethod())) {
            String fileName = null;
            try {
                fileName = this.setLogo(request, user);
            } catch (ActionGraspException e) {
                return BaseControllerHelper.createMessageResponse(e.getType(), e.getMessage());
            }

            String baseUrl = "/group/setting/logoCrop/" + fileName + "/" + id;
            String viewName = "redirect:" + BaseControllerHelper.generateUrl(baseUrl, FastHashMap.build(2)
                    .add("page", "backGroundLogoCrop").add("type", "background").toMap());
            return new ModelAndView(viewName);
        }

        Map<String, Object> group = groupService.getGroup(id);
        return new ModelAndView("group/setting-background")
                .addObject("groupinfo", group)
                .addObject("is_groupmember", this.getGroupMemberRole(id, user))
                .addObject("id", id)
                .addObject("logo", group.get("backgroundLogo"));
    }

    @RequestMapping("/group/{id}/member")
    public ModelAndView groupMemberAction(@PathVariable Integer id, HttpServletRequest request) {
        Map<String, Object> group = groupService.getGroup(id);

        if ("close".equalsIgnoreCase(String.valueOf(group.get("status")))) {
            return createMessageResponse("info", "该小组已被关闭");
        }

        AppUser user = AppUser.getCurrentUser(request);

        Map<String, Object> conditions = FastHashMap.build(2).add("groupId", id).add("role", "member").toMap();

        Paginator paginator = new Paginator(request,
                groupService.searchMembersCount(conditions), 30);

        List<Map<String, Object>> Members = groupService.searchMembers(conditions,
                OrderBy.build(1).addDesc("createdTime"),
                paginator.getOffsetCount(),
                paginator.getPerPageCount());

        Set<Object> memberIds = ArrayToolkit.column(Members, "userId");

        Map<String, AppUser> users = userService.findUsersByIds(memberIds);
        AppUser owner = userService.getUser(group.get("ownerId"));

        List<Map<String, Object>> groupAdmin = groupService.searchMembers(
                FastHashMap.build(2).add("groupId", id).add("role", "admin").toMap(),
                OrderBy.build(1).addDesc("createdTime"),
                0, 1000);

        Set<Object> groupAdminIds = ArrayToolkit.column(groupAdmin, "userId");
        Map<String, AppUser> usersLikeAdmin = userService.findUsersByIds(groupAdminIds);

        return new ModelAndView("/group/group-member")
                .addObject("groupinfo", group)
                .addObject("is_groupmember", this.getGroupMemberRole(id, user))
                .addObject("groupmember_info", Members)
                .addObject("owner_info", owner)
                .addObject("paginator", paginator)
                .addObject("members", users)
                .addObject("usersLikeAdmin", usersLikeAdmin)
                .addObject("groupAdmin", groupAdmin);
    }

    @RequestMapping("/group/info/edit/{id}")
    public ModelAndView groupEditAction(@PathVariable Integer id, HttpServletRequest request) {
        AppUser currentUser = AppUser.getCurrentUser(request);
        if (!this.checkManagePermission(id, currentUser)) {
            return createMessageResponse("info", "您没有权限!");
        }

        Map<String, Object> groupinfo = ParamMap.toFormDataMap(request);
        Map<String, Object> group = FastHashMap.build(2).toMap();
        if (MapUtil.isNotEmpty(groupinfo)) {
            group.put("title", groupinfo.get("group[grouptitle]"));
            group.put("about", groupinfo.get("group[about]"));
        }
        groupService.updateGroup(id, group);

        return new ModelAndView("redirect:/group/" +id);
    }

    @RequestMapping("/my/group/join")
    public String memberJoinAction(HttpServletRequest request, Model model) {
        AppUser user = AppUser.getCurrentUser(request);

        List<Map<String, Object>> admins = groupService.searchMembers(
                FastHashMap.build(2).add("userId", user.getId()).add("role", "admin").toMap(),
                OrderBy.build(1).addDesc("createdTime"), 0, 1000);

        List<Map<String, Object>> owners = groupService.searchMembers(
                FastHashMap.build(2).add("userId", user.getId()).add("role", "owner").toMap(),
                OrderBy.build(1).addDesc("createdTime"), 0, 1000);

        List<Map<String, Object>> members = Stream.concat(admins.stream(), owners.stream()).collect(Collectors.toList());
        Set<Object> groupIds = ArrayToolkit.column(members, "groupId");
        Map<String, Map<String, Object>> adminGroups = groupService.getGroupsByIds(groupIds);

        Paginator paginator = new Paginator(request,
                groupService.searchMembersCount(
                        FastHashMap.build(2).add("userId", user.getId()).add("role", "member").toMap()),
                12);

        members = groupService.searchMembers(
                FastHashMap.build(2).add("userId", user.getId()).add("role", "member").toMap(),
                OrderBy.build(1).addDesc("createdTime"),
                paginator.getOffsetCount(),
                paginator.getPerPageCount());

        groupIds = ArrayToolkit.column(members, "groupId");
        Map<String, Map<String, Object>> groups = groupService.getGroupsByIds(groupIds);

        model.addAttribute("user", user);
        model.addAttribute("adminGroups", adminGroups);
        model.addAttribute("paginator", paginator);
        model.addAttribute("groups", groups);
        return "/group/group-member-join";
    }
}
